配合分支:day28/start,Repo: 按我
沒有安全感,就把安全帽戴上!!其實這個專案默默寫了很多測試(包括 Android Test 和 Unit Test)!重新查看後發現一些弔詭的地方,例如這個測試:
fun export_readsOnceAndDoesNotSave_orChangeSourceEntries() {}
這個測試匯出的時候,只允許「讀取」不要有任何的寫入行為。主要透過一個假的 DiaryStore。丟進去真正的 DiaryExporter,再去檢查做 export() 時,搓了幾次 load() 搓了幾次 save()。
先來看這個假的 RecordingDiaryStore 在幹麻。雖然的確可以檢測 DiaryExporter 做了幾次讀取、寫入。

但這行是左手丟右手,怎麼測試都會過,無法保證最後結果。
val sourceEntries = listOf(firstEntry, secondEntry)
val originalEntries = sourceEntries.toList()
// WT? 💀
assertEquals(originalEntries, sourceEntries)
這次調整假 Class,讓這個 load() 真的做點什麼。這次調整的方向更變態,帶假資料,也一並測試 load() 後回傳的行為,只要查證資料,就可以不用記數器。(但是讀取還是要有)
不過 save() 就比較尷尬了,有別的方式驗證,但因為 interface 契約的關係,就先丟 Error 出去吧。(也會掛掉)

這段就可以作為 Source 餵進去就行了。
// Given:建立只讀匯出專用 fake,並記錄匯出前的來源集合
val diaryStore = ReadOnlyDiaryStore()
val originalEntries = diaryStore.sourceEntries.toList()
val diaryExporter = DiaryExporter(diaryStore)
由此一來,DiaryExporter 可以直接丟入想要測試的 Formatter,例如:純文字 Formatter。
// When:匯出已儲存日記
val exportedText = diaryExporter.export(PlainTextDiaryFormatter())
diaryExporter.export() 和 PlainTextDiaryFormatter() 做的都是 真實動作。而 ReadOnlyDiaryStore 則是剛剛放進去的木馬,透過它可以來驗證是否真的有進行讀取,或是寫入。
其實原版的邏輯大致上也是這樣,這次要調整的部分在於驗證。
這次有將 export() 出來的資料拿出來做對比。看一下 PlainTextDiaryFormatter() 做了啥。
override fun format(entries: List<DiaryEntry>): String {
return entries.joinToString(separator = "\n\n") { diaryEntry ->
"${diaryEntry.title}\n${diaryEntry.note}"
}
}
就是將 Title 以及 Note 串起來。exportedText 應該要長 第一筆\n第一筆內容\n\n第二筆\n第二筆內容這樣。那個(\n 是換行啦)。來寫驗證的部分囉。
// Then:確認輸出正確、只 load 一次,且來源集合與日記值不變
val expectedText = "第一筆\n第一筆內容\n\n第二筆\n第二筆內容"
// 👇🏼 驗證輸出的字,是否符合預期
assertEquals(expectedText, exportedText)
// 👇🏼 Load 只做了一次
assertEquals(1, diaryStore.loadCallCount)
// 🤗 上面存了 originalEntries,而且是複製了一份的
// 兩個 List 要一樣
assertEquals(originalEntries, diaryStore.sourceEntries)
// 第一個 Data 要相同
assertEquals(originalEntries[0], diaryStore.sourceEntries[0])
// 第二個 Data 要相同
assertEquals(originalEntries[1], diaryStore.sourceEntries[1])
可以看出來更堅固了!最後就來跑跑看是否成功:
